﻿#region

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using log4net.Core;
using log4net.Repository.Hierarchy;
using Tateeda.ClinicalStudy.Base.Data.DBModel;
using Tateeda.ClinicalStudy.Base.Data.EF;
using Tateeda.ClinicalStudy.Base.Modules.Common.Enumeration;
using Tateeda.ClinicalStudy.Base.Modules.Common.Extenstions;
using Tateeda.ClinicalStudy.Base.Modules.Infrastructure.Interfaces.Services;
using Tateeda.ClinicalStudy.Base.Modules.Models.Schedule;
using Tateeda.ClinicalStudy.Base.Modules.Models.StudyVisit;
using Tateeda.ClinicalStudy.Base.Modules.Models.User;

#endregion

#region - Legal -

///<legal>
/// Copyright © 2011, Tateeda Media Network
/// All rights reserved. http://tateeda.com
///
/// Redistribution and use in source and binary forms, with or without
/// modification, are permitted provided that the following conditions
/// are met:
///
/// - Redistributions of source code must retain the above copyright
/// notice, this list of conditions and the following disclaimer.
///
/// - Neither the name of the Tateeda Media Network, nor the names of its
/// contributors may be used to endorse or promote products
/// derived from this software without specific prior written
/// permission.
///
/// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
/// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
/// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
/// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
/// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
/// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES INCLUDING,
/// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
/// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
/// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
/// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
/// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
/// POSSIBILITY OF SUCH DAMAGE.
///</legal>

#endregion - Legal -

namespace Tateeda.ClinicalStudy.Base.Modules.Infrastructure.Concrete.Services {

    public class SchedulingService : ISchedulingService {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IApplicationService _applicationService;

        public SchedulingService() {
            _unitOfWork = new SqlUnitOfWork();
            _applicationService = new ApplicationService();
        }

        #region - ISchedulingService Members -

        public virtual IUnitOfWork UnitOfWork {
            get { return _unitOfWork; }
        }

        public ICollection<SubjectModel> GetActiveSubjectsForSite(int siteId) {
            var subjectService = new SubjectService();
            return subjectService.GetSiteSubjects(siteId);
        }

        public ICollection<SubjectModel> GetActiveSubjectsForSiteFast(int siteId) {
            //var subs =
            //    UnitOfWork.Subjects.Where(s => s.SiteId == siteId && s.Status == (int)StudyStatus.ActiveStatus).ToList();

            var subjects = UnitOfWork.Context.uspGetSubjectBySiteId(siteId);

            return subjects.Select(s => new SubjectModel {
                SubjectId = s.SubjectId,
                SiteId = siteId,
                SortOrder = s.SortOrder,
                FirstName = s.FirstName,
                LastName = s.LastName,
                DateOfBirth = s.DateOfBirth,
                FamilyId = s.FamilyId,
                Gender = s.Gender,
                Nickname = s.Nickname
            }).ToList();
        }

        public ICollection<VisitModel> GetAvailableVisitsForSubject(int subjectId) {
            if (subjectId > 0) {
                var subject = UnitOfWork.Subjects.Where(s => s.SubjectId == subjectId).SingleOrDefault();
                return subject != null ? subject.Visits.ToList().ToVisitModelList() : new Collection<VisitModel>();
            } return new Collection<VisitModel>();
        }

        #endregion

        #region ISchedulingService Members

        /// <summary>
        /// Gets the calendar appointments by site.
        /// </summary>
        /// <param name="appUser">The app user.</param>
        /// <param name="startDate">The start date.</param>
        /// <param name="endDate">The end date.</param>
        /// <returns></returns>
        public ICollection<ScheduleModel> GetCalendarAppointmentsBySite(AppUserModel appUser, DateTime startDate, DateTime endDate) {
            var list = new Collection<ScheduleModel>();
            var appointments = UnitOfWork.Appointments.Where(a => a.SiteId == appUser.SiteId && a.StartTime >= startDate && a.EndTime <= endDate).
                 ToList();
            const string color = "Black";
            foreach (var appt in appointments) {
                var schedule = new ScheduleModel {
                    allDay = appt.StartTime.Equals(appt.EndTime),
                    AppUserId = appUser.AppUserId,
                    Color = color,
                    StartDate = _applicationService.ConvertTimeFromUTC(appt.AppUserId, appt.StartTime),
                    EndDate = _applicationService.ConvertTimeFromUTC(appt.AppUserId, appt.EndTime),
                    title = string.Format("Subject:{0} with {1} {2} \nVisit:{3}", appt.SubjectId, appt.AppUser.FirstName, appt.AppUser.LastName, appt.Visit.Name),
                    id = appt.AppointmentId,
                    SubjectId = appt.SubjectId,
                    Descritpion = appt.Description
                };

                list.Add(schedule);
                Console.Out.WriteLine("start from appointment: {0} converted to {1} start: {2}", appt.StartTime, schedule.StartDate, schedule.start);
            }

            return list;
        }

        /// <summary>
        /// Updates the appointment.
        /// !Dates are in UTC Format
        /// </summary>
        /// <param name="appointmentId">The appointment id.</param>
        /// <param name="title">The title.</param>
        /// <param name="startTime">The start time.</param>
        /// <param name="endTime">The end time.</param>
        public void UpdateAppointment(int appointmentId, string title, DateTime startTime, DateTime endTime) {
            var appt = UnitOfWork.Appointments.Where(a => a.AppointmentId == appointmentId).SingleOrDefault();
            if (appt != null) {
                appt.Title = title;
                appt.StartTime = startTime.ToUniversalTime();
                appt.EndTime = endTime.ToUniversalTime();
                appt.UpdatedOn = DateTime.UtcNow;
            }

            UnitOfWork.Commit();
        }

        /// <summary>
        /// Creates the appointment.
        /// </summary>
        /// <param name="subjectId">The subject id.</param>
        /// <param name="visitId">The visit id.</param>
        /// <param name="appUserId">The app user id.</param>
        /// <param name="title">The title.</param>
        /// <param name="startTime">The start time.</param>
        /// <param name="endTime">The end time.</param>
        /// <param name="description">The description.</param>
        /// <param name="scheduledBy">The scheduled by.</param>
        /// <returns></returns>
        public int CreateAppointment(int subjectId, int visitId, int appUserId, string title, DateTime startTime, DateTime endTime, string description, string scheduledBy) {
            var appUser = UnitOfWork.AppUsers.Where(au => au.AppUserId == appUserId).FirstOrDefault();
            if (appUser == null) return -1;

            var appt = new Appointment {
                SubjectId = subjectId,
                VisitId = visitId,
                AppUserId = appUserId,
                Title = title,
                Description = description,
                StartTime = startTime.ToUniversalTime(),
                EndTime = endTime.ToUniversalTime(),
                SiteId = appUser.SiteId,
                ScheduledBy = scheduledBy,
                CreatedOn = DateTime.UtcNow,
                Status = StudyStatus.NotStartedStatus.ToStatusInt()
            };

            UnitOfWork.Appointments.AddObject(appt);
            UnitOfWork.Commit();
            return appt.AppointmentId;
        }

        /// <summary>
        /// Deletes the appointment.
        /// </summary>
        /// <param name="appointmentId">The appointment id.</param>
        /// <returns></returns>
        public bool DeleteAppointment(int appointmentId) {
            try {
                var appt = UnitOfWork.Appointments.Where(ap => ap.AppointmentId == appointmentId).FirstOrDefault();
                UnitOfWork.Appointments.DeleteObject(appt);
                UnitOfWork.Commit();
                return true;
            } catch {
                return false;
            }
        }

        #endregion
    }
}